import socket as Socket
#from drivers import Relay
from time import sleep
from os import stat
import gc
#import uasyncio as asyncio
from webservice.socketserviceresponsebuilder import SocketServiceResponseBuilder as ResponseBuilder
from webservice.socketserviceconst import SocketServiceConst

class SocketService:
	u"""
	Socket 服务器类
	"""

	def __init__(self, timercontroller):
		u"""
		实例化 Socket 服务器
		"""
		# 定义服务器套接字
		self._socket_server = Socket.socket()
		self._connection = None
		self._timercontroller = timercontroller

		#self._websocket_server = Socket.socket()
		#self._ws_connection = None

	def init(self, port: int = 8266):
		u"""
		初始化服务器，绑定服务器地址和端口，并启动监听
		:param port: 默认端口号8266
		:return: 无
		"""

		if (self._socket_server is not None):
			self._socket_server.close()
			self._socket_server = None

		address = Socket.getaddrinfo('0.0.0.0', port)[0][-1]

		self._socket_server = Socket.socket()
		self._socket_server.setsockopt(Socket.SOL_SOCKET, Socket.SO_REUSEADDR, 1)
		self._socket_server.bind(address)
		self._socket_server.listen(5)
		self._socket_server.setblocking(False)

		print("\nServer started and listening on", address)

		#if (self._websocket_server is not None):
		#	self._websocket_server.close()
		#	self._websocket_server = None

		#ws_address = Socket.getaddrinfo('0.0.0.0', 6288)[0][-1]

		#self._websocket_server = Socket.socket()
		#self._websocket_server.setsockopt(Socket.SOL_SOCKET, Socket.SO_REUSEADDR,1)
		#self._websocket_server.bind(ws_address)
		#self._websocket_server.listen(5)
		#self._socket_server.setblocking(False)

	def check_client_request_cb(self, timer):
		u"""
		服务端核心模块，用于接收客户端请求，并根据请求执行相应操作
		:param timer: 无用
		:return: 无
		"""

		try:
			if (self._connection is None):
				self._connection, address = self._socket_server.accept()

				print("client connected from", address)
			else:
				request = self._connection.recv(1024)
				#gc.collect()

				# 客户端断开连接会发送空字节
				if (request == b''):
					self._close_connection()

					print("connection closed")
				else:
					request_first_line = str(request.split(b'\r\n')[0], "utf-8")

					print("- client request:", request_first_line)

					if (request_first_line.startswith("GET /")):
						request_method, request_file = request_first_line.split(" ")[:2]

						if (len(request_file.split("?")) > 1):
							self._respond_to_js(request_method, request_file)
						else:
							self._respond_to_browser(request_method, request_file)
					else:
						self._respond_to_client(request_first_line)

				gc.collect()
		except Exception as ex:
			if (str(ex) == "[Errno 11] EAGAIN"):
				pass
			elif ((str(ex) == "[Errno 104] ECONNRESET") or (str(ex) == "[Errno 9] EBADF")):
				self._close_connection()
				gc.collect()
				self.init()
			else:
				print(ex.args)
				raise ex

	def _respond_to_client(self, reqeust: str):
		if (reqeust.find(SocketServiceConst.COMMAND_ACTIVE_RELAY) != -1):
			if (not self._timercontroller.get_relay_status):
				self._timercontroller.active_relay()
		elif (reqeust.find(SocketServiceConst.COMMAND_DEACTIVE_RELAY) != -1):
			if (self._timercontroller.get_relay_status):
				self._timercontroller.deactive_relay()
		elif (reqeust.find(SocketServiceConst.COMMAND_ENABLE_AUTO_RELAY) != -1):
			if (self._timercontroller.get_relay_status):
				self._timercontroller.deactive_relay()

			self._timercontroller.start_distance_info_timer()
		elif (reqeust.find(SocketServiceConst.COMMAND_DISABLE_AUTO_RELAY) != -1):
			self._timercontroller.stop_distance_info_timer()
		elif (reqeust.find(SocketServiceConst.COMMAND_QUERY_DISTANCE_STATUS) != -1):
			self._send_response(self._timercontroller.get_distance_status)
			gc.collect()
		else:
			print("ready to send readme")

			self._send_response(SocketServiceConst.RESPOND_FOR_CLIENT_README)
			gc.collect()

	def _respond_to_js(self, request_methon, request_commands: str):
		if (request_methon == "GET"):
			response = ResponseBuilder() #ResponseBuilder()
			commands = request_commands.split("?")[1]

			if (commands.find("&") != -1):
				array_temp = commands.split("&")

				command = array_temp[0].split("=")
				action = command[1]
				command = command[0]

				param_string = array_temp[1:]
				params = self._parse_url_params(param_string)
			else:
				command = commands.split("=")[0]
				action = commands.split("=")[1]

			response_text = '{"error": "invalid command"}'

			if (command == "query_command"):
				if (action == "get_status"):
					relay_status = ("on" if (self._timercontroller.get_relay_status) else "off")
					distance_mode = ("auto" if (self._timercontroller.get_distance_status) else "manual")

					import network

					access_point = network.WLAN(network.AP_IF)

					response_text = '{"relay_status": "%s", "distance_mode": "%s", "ip_address": "%s"}' % (relay_status, distance_mode, access_point.ifconfig()[0])

					gc.collect()
				elif (action == "turn_light"):
					self._timercontroller.active_relay() if not self._timercontroller.get_relay_status else self._timercontroller.deactive_relay()

					relay_status = ("on" if (self._timercontroller.get_relay_status) else "off")

					response_text = '{"relay_status": "%s"}' % relay_status
				elif (action == "change_mode_to_auto"):
					self._timercontroller.start_distance_info_timer()

					distance_mode = ("auto" if (self._timercontroller.get_distance_status) else "manual")

					response_text = '{"distance_mode": "%s"}' % distance_mode
				elif (action == "change_mode_to_manual"):
					self._timercontroller.stop_distance_info_timer()

					distance_mode = ("auto" if (self._timercontroller.get_distance_status) else "manual")

					response_text = '{"distance_mode": "%s"}' % distance_mode
				elif (action == "set_ap_mode"):
					from others import wifi_handler

					#new_access_point = wifi_handler.Wlan()
					wifi_handler.set_ap_mode(params["ssid"], params["password"])

					#print(params)
					gc.collect()
				elif (action == "set_sta_mode"):
					from others import wifi_handler

					result_code, result_msg, ip_address = wifi_handler.set_sta_mode(params["ssid"], params["password"])

					response_text = '{"result_code": %s, "result_msg": "%s", "ip_address": "%s"}' % (result_code, result_msg, ip_address)

					#print(params)
					gc.collect()

			response.set_result_status()
			response.set_content_length(len(response_text))

			print("- send js response header")
			self._send_response(response.generate_response_header())
			gc.collect()

			print("- send js response data")
			self._send_response(response_text)
			gc.collect()

			print("- js response send ok")

			sleep(0.05)
			self._close_connection()
			print("client connection closed manually\n")

	def _respond_to_browser(self, request_method: str, request_file: str):
		print("- request file:", request_file)

		if (request_method == "GET"):
			response = ResponseBuilder()

			if (request_file == "/"):
				filename = SocketServiceConst.WEB_ROOT + "/index.html"
			elif (request_file == "/favicon.ico"):
				filename = SocketServiceConst.WEB_ROOT + "/icon.ico"
			else:
				filename = SocketServiceConst.WEB_ROOT + request_file

			filesize = stat(filename)[6]

			print("-- file name:", filename)
			print("-- file size:", filesize)

			#sleep(0.03)

			with (open(filename, "rb", 0)) as file:
				response.set_result_status()
				response.set_content_length(filesize)

				if (filename.endswith(".ico")):
					response.set_content_type("image/x-icon")
				elif (filename.endswith(".css")):
					response.set_content_type("text/css")
				elif (filename.endswith(".png")):
					response.set_content_type("image/png")
				elif (filename.endswith(".js")):
					response.set_content_type("application/javascript")

				print("- send response header")
				self._send_response(response.generate_response_header())
				gc.collect()

				total_send_count = 0

				print("- send response file")

				while True:
					data = file.read(256)
					data_length = len(data)

					if (data_length > 0):
						send_count = self._send_response(data)
						total_send_count += send_count

						if (send_count != data_length):
							#print("--- (%s/%s)" % (send_count, data_length))
							print("--- retry")

							sleep(0.05)
							gc.collect()
							total_send_count += self._send_response(data[send_count:])

						sleep(0.05)
						gc.collect()
					else:
						break

				print("- file send ok")
				#print("- file send %s bytes" % total_send_count)

			sleep(0.05)
			self._close_connection()
			print("client connection closed manually\n")

	@staticmethod
	def _parse_url_params(param_string):
		params = {}

		for param in param_string:
			paramm = param.split("=")
			params[paramm[0]] = paramm[1]

		return params

	def _send_response(self, response: any):
		result = 0

		if (len(response) > 0):
			if (type(response) is str):
				result = self._connection.send(bytes(response, "utf-8"))
			else:
				result = self._connection.send(response)

		return result

	def _close_connection(self):
		if (self._connection is not None):
			self._connection.close()
			self._connection = None
